<template>
<div style="margin-top: 20px">
  <a-row justify="center">
    <a-col :xs="{ span: 23 }" :sm="{ span: 23 }" :md="{ span: 23 }" :lg="{ span: 22 }" :xl="{ span: 19 }" :xxl="{ span: 16 }">
      <div class="go-back" @click="goBack"><double-left-outlined /><span>&nbsp;返回</span></div>
    </a-col>
  </a-row>
  <div style="text-align: center">
    <h1>随心刷题</h1>
  </div>
  <a-back-top />
  <a-row justify="center">
    <a-col :xs="{ span: 23 }" :sm="{ span: 23 }" :md="{ span: 23 }" :lg="{ span: 22 }" :xl="{ span: 19 }" :xxl="{ span: 16 }">
<!--      <div class="wrapper">-->
<!--        <span>类型:&nbsp;</span>-->
<!--        <a-select style="width: 80px" v-model:value="qusType">-->
<!--          <a-select-option value="408">408</a-select-option>-->
<!--          <a-select-option value="803">803</a-select-option>-->
<!--        </a-select>-->
<!--      </div>-->
      <div class="wrapper">
        <span>科目:&nbsp;</span>
        <a-select
          style="width: 180px"
          v-model:value="subject"
          @change="onSubjectChange"
          mode="multiple"
          :maxTagCount="1"
          :maxTagTextLength="2"
          placeholder="选择科目"
        >
          <a-select-option value="CS">数据结构</a-select-option>
          <a-select-option value="CCP">组成原理</a-select-option>
          <a-select-option value="OS">操作系统</a-select-option>
          <a-select-option value="NET">计算机网络</a-select-option>
        </a-select>
      </div>
      <div class="wrapper">
        <span>标签:&nbsp;</span>
        <a-select
          style="width: 180px"
          :options="cateSubOption"
          :field-names="{ label: 'name', value: 'id', options: 'children' }"
          :maxTagCount="1"
          :virtual="true"
          mode="multiple"
          placeholder="选择分类"
          @change="checkTagCount"
          v-model:value="tag">
        </a-select>
      </div>
      <div class="wrapper">
        <span>年份:&nbsp;</span>
        <a-select style="width: 80px" v-model:value="year" :options="yearOptions" @change="onYearChange"></a-select>
      </div>
      <div class="wrapper">
        <span>题号:&nbsp;</span>
        <a-input-number v-model:value="number" :min="1" :max="40"></a-input-number>
      </div>
      <div class="wrapper">
        <a-button primary @click="queryQuestions">搜索</a-button>
      </div>
    </a-col>
    <a-col :xs="{ span: 23 }" :sm="{ span: 23 }" :md="{ span: 23 }" :lg="{ span: 22 }" :xl="{ span: 19 }" :xxl="{ span: 16 }">
      <div style="margin-top: 20px; margin-left: 8px; font-size: 1.5em; letter-spacing: 2px">
        <span>共检索到: {{ qusIds.length }} 题; 已加载 {{ questions.length }} 题</span>
      </div>
    </a-col>
  </a-row>
  <a-divider>题目列表</a-divider>
  <a-row justify="center">
    <a-col :xs="{ span: 23 }" :sm="{ span: 23 }" :md="{ span: 23 }" :lg="{ span: 22 }" :xl="{ span: 18 }" :xxl="{ span: 14 }">
      <a-list :dataSource="questions">
        <template #loadMore>
          <div class="loading-more">
            <a-button @click="loadMore" type="primary" ghost :disabled="loading"><loading-outlined v-show="loading"/>加载更多</a-button>
          </div>
        </template>
        <template #renderItem="{ item, index }">
          <div class="problem" :class="{ 'answer-right': solveLog.has(item.id) && solveLog.get(item.id).status === 1, 'answer-wrong': solveLog.has(item.id) && solveLog.get(item.id).status === 0 }">
            <div>
              <a-tag color="purple">{{ item.type }}</a-tag>
              <a-tag color="green">{{ item.year }} 第 {{ item.number }} 题</a-tag>
              <check-circle-filled style="color: #2ecc71;margin-left: 2px;margin-right: 2px"/><span>{{ item.acc }}%</span>
              <edit-filled style="color: #34495e;margin-left: 3px;margin-right: 2px"/><span>{{ item.try }}</span>
              <alert-filled style="color: #f8cd0b;margin-left: 10px;margin-right: 3px; font-size: 1.2em"/><span class="show-ans" @click="showAns(item.id, index)">解析</span>
            </div>
            <div style="margin-top: 4px">
              <a-tag color="cyan" v-for="(v, idx) in item.tag" :key="idx">{{v}}</a-tag>
            </div>
            <ShowProblem :content="item.question"/>
            <div style="margin-top: 15px">
              <a-radio-group button-style="solid" v-model:value="userChoice[index]" :disabled="solveLog.has(item.id)">
                <a-radio-button value="A">A</a-radio-button>
                <a-radio-button value="B">B</a-radio-button>
                <a-radio-button value="C">C</a-radio-button>
                <a-radio-button value="D">D</a-radio-button>
              </a-radio-group>
            </div>
            <div class="answer-pad" v-for="(ans, idx) in item.answer" :key="idx" v-show="isShowAns.has(item.id) && isShowAns.get(item.id)">
              <hr>
              <div class="provider">
                <copyright-circle-outlined style="color: #9b59b6"/>
                <span>{{ ans.author }}</span>
              </div>
              <ShowProblem :content="ans.content" />
            </div>
          </div>
        </template>
      </a-list>
      <div style="margin-top: 30px;text-align: center;">
        <a-button type="primary" ghost danger @click="judgeQus">一键批阅</a-button>
      </div>
    </a-col>
  </a-row>
  <div style="height: 10px"></div>
  <AFooter />
</div>
</template>

<script lang="ts">
import {
  defineComponent, ref, reactive, onMounted, onUnmounted, onBeforeUnmount
} from 'vue'
import { useRouter } from 'vue-router'
import { Row, Col, Select, InputNumber, Button, List, Radio, Tag, Divider, message, BackTop } from 'ant-design-vue'
import ShowProblem from '@/components/ShowProblem.vue'
import { CheckCircleFilled, EditFilled, AlertFilled, CopyrightCircleOutlined, LoadingOutlined, DoubleLeftOutlined } from '@ant-design/icons-vue'
import { AnsSeq, SUBJECT_CATE_KEY, AnsFieldSeq } from '@/utils/constants'
import AFooter from '@/components/Footer.vue'
import type { SelectProps } from 'ant-design-vue'
import {
  getSubjectCate,
  getAllSubjectAllID,
  getQusByYear,
  getQusBySubjectTag,
  getQusByYearNumberV2,
  getTargetQus,
  addRecordLog
} from '@/service'

import { checkToken, decodeCT, decodeID, decodeValue } from '@/utils/check'
import { transUrl, KillRecord, SolveLog } from '@/utils/ds'

interface AnsPair {
  author: string,
  content: string
}

interface Question {
  id: number,
  type: string,
  year: string,
  number: number,
  subject: string,
  acc: number,
  try: number,
  tag: string[],
  question: string,
  choice:string
  answer: AnsPair[]
}

interface Option {
  name: string,
  id: string
}

interface OptionV2 {
  value: string,
  label: string
}

interface SubjectCateOption {
  CS: Option[],
  OS: Option[],
  CCP: Option[],
  NET: Option[]
}

export default defineComponent({
  name: 'UserDefineQus',
  components: {
    AFooter,
    DoubleLeftOutlined,
    LoadingOutlined,
    CheckCircleFilled,
    CopyrightCircleOutlined,
    AlertFilled,
    EditFilled,
    ADivider: Divider,
    ARow: Row,
    ACol: Col,
    ATag: Tag,
    ASelect: Select,
    ASelectOption: Select.Option,
    AInputNumber: InputNumber,
    AButton: Button,
    AList: List,
    ARadioGroup: Radio.Group,
    ARadioButton: Radio.Button,
    ABackTop: BackTop,
    ShowProblem
  },
  setup () {
    const router = useRouter()
    const loading = ref<boolean>(false)
    const loadCursor = ref<number>(0)
    const qusType = ref<string | null>('408')
    const subject = ref<string[]>([])
    const tag = ref<string[]>([])
    const year = ref<string | null>(null)
    const number = ref<number | null>(null)
    const questions = reactive<Question[]>([])
    const subjectCate = ref<SubjectCateOption>({
      CS: [],
      OS: [],
      CCP: [],
      NET: []
    })
    const cateOptions = ref<Option[]>([])
    const yearOptions = reactive<OptionV2[]>([])
    const cateSubOption = ref<SelectProps['options']>([
      {
        id: 'CS',
        name: '数据结构',
        children: []
      },
      {
        id: 'OS',
        name: '操作系统',
        children: []
      },
      {
        id: 'CCP',
        name: '组成原理',
        children: []
      },
      {
        id: 'NET',
        name: '计算机网络',
        children: []
      }
    ])
    const qusIds = ref<string[]>([])
    const isShowAns = ref<Map<number, boolean>>(new Map<number, boolean>())
    const userChoice = ref<string[]>([])
    const solveLog = reactive<Map<number, KillRecord>>(new Map<number, KillRecord>())
    const lastLoadTime = ref<number>(0)

    const setupYearOption = () => {
      const today = new Date()
      const year = today.getFullYear()
      for (let i = year - 1; i >= 2009; i--) {
        yearOptions.push({ label: i.toString(), value: i.toString() })
      }
    }

    const initUserChoice = (n: number) => {
      userChoice.value.length = 0
      for (let i = 0; i < n; i++) {
        userChoice.value.push('X')
      }
    }

    const submitDataOnLeave = () => {
      if (solveLog.size > 0) {
        const token:string = checkToken()
        const uid = decodeID(token)
        if (uid <= 0) {
          return
        }
        const data: SolveLog[] = []
        solveLog.forEach((v) => {
          data.push({
            uid: uid,
            pid: v.pid,
            status: v.status,
            created: v.created
          })
        })
        addRecordLog(data)
          .then((response) => {
            if (response.status !== 200) {
              message.warn('上传记录请求出错')
              return
            }
            const resp = response.data
            if (resp.code !== 0) {
              message.warn('上传记录出错')
            }
          })
      }
    }

    const checkTagCount = () => {
      if (tag.value.length > 5) {
        const t = tag.value.pop()
        if (t !== undefined) {
          const a = t.split('_')[1]
          message.warn(`最多选择5个标签，标签【${a}】被丢弃`)
        } else {
          message.warn('最多选择5个标签')
        }
      }
    }

    const queryQuestions = () => {
      submitDataOnLeave()
      solveLog.clear()
      qusIds.value.length = 0
      questions.length = 0
      loadCursor.value = 0
      if (number.value !== null) {
        if (year.value === null) {
          message.info('请设置年份')
          return
        }
        // 按照年份和题号搜题
        const qusT = qusType.value === null ? '408' : qusType.value
        getQusByYearNumberV2(year.value, number.value.toString(), qusT)
          .then(response => {
            if (response.status !== 200) {
              message.error('查询出错')
              return
            }
            const resp = response.data
            if (resp.code !== 0 || resp.data === null) {
              message.warn(`未找到${year.value}年第${number.value}题`)
              return
            }
            const data = resp.data
            const content = decodeQusAns(data.lk, data.ct, data.rt)
            if (content === undefined) {
              return
            }
            const decodeQus = rebuildQus(content)
            if (decodeQus !== undefined) {
              questions.push(decodeQus)
              initUserChoice(1)
              qusIds.value.push(data.id)
              loadCursor.value = 1
            }
          })
          .catch(() => {
            message.error('通过年份和题号搜题出错')
          })
        return
      }

      // 按照年份搜索题目ID
      if (year.value !== null) {
        getQusByYear(year.value)
          .then(response => {
            if (response.status !== 200) {
              message.error('请求失败')
              return
            }
            const resp = response.data
            if (resp.code !== 0 || resp.data === null) {
              message.warn('为找到对用记录')
              return
            }
            const data = resp.data
            qusIds.value.push(...data)
            initUserChoice(qusIds.value.length)
            // 触发加载题目
            loadMore()
          })
        return
      }

      if (subject.value.length === 0) {
        message.info('学科为空')
        return
      }

      // 按照学科搜题
      if (tag.value.length === 0) {
        getAllSubjectAllID(subject.value)
          .then(response => {
            if (response.status !== 200) {
              message.error('请求失败')
              return
            }
            const resp = response.data
            if (resp.code !== 0 || resp.data === null) {
              message.warn('未找到对应记录')
              return
            }
            const data = resp.data
            if (data.length < 2) {
              message.warn('未找到对应记录')
              return
            }

            const lk = decodeID(data[0])
            for (let i = 1; i < data.length; i++) {
              const c = decodeValue(data[i], lk)
              const s = c.split('_')
              if (s.length > 1) {
                const o = s[1].split('.')
                qusIds.value.push(...o)
              }
            }
            initUserChoice(qusIds.value.length)
            loadMore()
          })
        return
      }

      // 按照学科和标签搜题
      getQusBySubjectTag(tag.value)
        .then(response => {
          if (response.status !== 200) {
            message.error('请求失败')
            return
          }
          const resp = response.data
          if (resp.code !== 0 || resp.data === null) {
            message.warn('未找到对用记录')
            return
          }
          const data = resp.data
          const lk = decodeID(data.lk)
          for (const k in data) {
            if (k === 'lk') {
              continue
            }
            const id = decodeValue(data[k], lk)
            const idSplit = id.split('.')
            qusIds.value.push(...idSplit)
          }
          initUserChoice(qusIds.value.length)
          loadMore()
        })
    }

    const setUpCateOptions = () => {
      const cate = sessionStorage.getItem(SUBJECT_CATE_KEY)
      if (cate !== null) {
        subjectCate.value = JSON.parse(cate)
        return
      }
      getSubjectCate()
        .then((response) => {
          if (response.status !== 200) {
            message.error('请求学科类别出错')
          }
          const resp = response.data
          const data = resp.data
          if (data === null) {
            return
          }
          const cs = data.CS
          const os = data.OS
          const ccp = data.CCP
          const net = data.NET
          cs.forEach((v: string) => {
            if (v === 'acc' || v === 'num') {
              return
            }
            subjectCate.value.CS.push({ name: v, id: `CS_${v}` })
          })

          os.forEach((v: string) => {
            if (v === 'acc' || v === 'num') {
              return
            }
            subjectCate.value.OS.push({ name: v, id: `OS_${v}` })
          })

          ccp.forEach((v: string) => {
            if (v === 'acc' || v === 'num') {
              return
            }
            subjectCate.value.CCP.push({ name: v, id: `CCP_${v}` })
          })

          net.forEach((v: string) => {
            if (v === 'acc' || v === 'num') {
              return
            }
            subjectCate.value.NET.push({ name: v, id: `NET_${v}` })
          })

          sessionStorage.setItem(SUBJECT_CATE_KEY, JSON.stringify(subjectCate.value))
        })
        .catch(() => {
          message.error('请求失败')
        })
    }

    const onSubjectChange = (curSubject:string[]) => {
      tag.value.length = 0
      year.value = null
      number.value = null
      const t = cateSubOption.value
      if (t === undefined) {
        return
      }
      for (let i = 0; i < t.length; i++) {
        const c = t[i]
        if (c !== undefined && c.children !== undefined) {
          c.children = []
        }
      }
      curSubject.forEach((v: string) => {
        if (v === 'CS') {
          if (t !== undefined && t[0].children?.length === 0) {
            t[0].children = subjectCate.value.CS
          }
        } else if (v === 'OS') {
          if (t !== undefined && t[1].children?.length === 0) {
            t[1].children = subjectCate.value.OS
          }
        } else if (v === 'CCP') {
          if (t !== undefined && t[2].children?.length === 0) {
            t[2].children = subjectCate.value.CCP
          }
        } else if (v === 'NET') {
          if (t !== undefined && t[3].children?.length === 0) {
            t[3].children = subjectCate.value.NET
          }
        }
      })
    }

    const showAns = (id: number, index: number) => {
      const v = isShowAns.value.get(id)
      if (v === undefined) {
        isShowAns.value.set(id, true)
      } else {
        isShowAns.value.set(id, !v)
      }
      const right = questions[index].choice === userChoice.value[index]
      solveLog.set(id, {
        select: userChoice.value[index],
        status: right ? 1 : 0,
        created: new Date().getTime(),
        pid: id
      })
    }

    const rebuildQus = (decoded: Map<string, string> | undefined) => {
      if (decoded === undefined) {
        return
      }
      const rId = decoded.get('id')
      const rType = decoded.get('type')
      const rYear = decoded.get('year')
      const rNumber = decoded.get('number')
      const rSubject = decoded.get('subject')
      const rTry = decoded.get('try')
      const rQuestion = decoded.get('question')
      const rChoice = decoded.get('choice')
      const rAcc = decoded.get('acc')
      const rTag = decoded.get('tag')
      const rAnswer = decoded.get('answer')

      const decodedQus:Question = {
        id: parseInt(rId === undefined ? '0' : rId),
        type: rType === undefined ? '408' : rType,
        year: rYear === undefined ? '' : rYear,
        number: parseInt(rNumber === undefined ? '0' : rNumber),
        subject: rSubject === undefined ? '' : rSubject,
        acc: parseInt(rAcc === undefined ? '0' : rAcc),
        try: parseInt(rTry === undefined ? '0' : rTry),
        tag: [],
        question: rQuestion === undefined ? '' : transUrl(rQuestion),
        choice: rChoice === undefined ? 'X' : rChoice,
        answer: []
      }
      if (rTag !== undefined) {
        const t = rTag.split('#')
        decodedQus.tag.push(...t)
      }
      if (rAnswer !== undefined) {
        const a = rAnswer.split(AnsSeq)
        a.forEach((v: string) => {
          const f = v.split(AnsFieldSeq)
          decodedQus.answer.push({ content: transUrl(f[0]), author: f[1] })
        })
      }
      return decodedQus
    }

    const loadMore = () => {
      if (qusIds.value.length === 0) {
        message.info('请设置筛选条件并点击搜索')
        return
      }
      if (loadCursor.value >= qusIds.value.length) {
        message.info('已加载所有问题')
        return
      }
      loading.value = true
      const pre = lastLoadTime.value
      const cur = Math.ceil(new Date().getTime() / 1000)
      if (pre !== 0 && cur - pre <= 10) {
        setTimeout(() => {
          loading.value = false
          message.warn('通信繁忙，请稍后重试')
        }, 1000)
        return
      }
      lastLoadTime.value = cur
      const batchSize = 5
      const ids = qusIds.value
      const start = loadCursor.value
      loadCursor.value = Math.min(ids.length, start + batchSize)
      const arr:number[] = []
      for (let i = start; i < loadCursor.value; i++) {
        arr.push(parseInt(ids[i]))
      }
      if (arr.length === 0) {
        loading.value = false
        return
      }
      getTargetQus(arr)
        .then((response) => {
          if (response.status !== 200) {
            message.error('请求出错')
            loading.value = false
            return
          }
          const resp = response.data
          if (resp.code !== 0) {
            message.error('获取数据出错')
            loading.value = false
            return
          }
          if (resp.data === null) {
            message.error('获取数据出错')
            loading.value = false
            return
          }
          const data = resp.data
          for (let i = 0; i < data.length; i++) {
            const qus = data[i]
            const decoded = decodeQusAns(qus.lk, qus.ct, qus.rt)
            const decodedQus = rebuildQus(decoded)
            if (decodedQus !== undefined) {
              questions.push(decodedQus)
            }
          }
          loading.value = false
        })
        .catch(err => {
          loading.value = false
          console.log(err)
        })
    }

    const decodeQusAns = (lk: string, ct: string, rt: string) => {
      const k = decodeID(lk)
      const header = decodeValue(rt, k)
      return decodeCT(header, ct, k)
    }

    const judgeQus = () => {
      const u = Math.min(loadCursor.value, qusIds.value.length)
      for (let i = 0; i < u; i++) {
        const id = questions[i].id
        if (solveLog.has(id)) {
          continue
        }
        const status = userChoice.value[i] === questions[i].choice ? 1 : 0
        const choice = userChoice.value[i]
        const created = new Date().getTime()
        solveLog.set(id, {
          pid: id,
          created: created,
          status: status,
          select: choice
        })
      }
    }

    const goBack = () => {
      router.back()
    }

    const onYearChange = () => {
      subject.value.length = 0
      tag.value.length = 0
    }

    onMounted(() => {
      setupYearOption()
      setUpCateOptions()
      window.addEventListener('beforeunload', submitDataOnLeave)
    })

    onBeforeUnmount(() => {
      submitDataOnLeave()
    })

    onUnmounted(() => {
      window.removeEventListener('beforeunload', submitDataOnLeave)
    })

    return {
      loading,
      qusType,
      subject,
      tag,
      year,
      number,
      questions,
      qusIds,
      queryQuestions,
      yearOptions,
      onSubjectChange,
      cateSubOption,
      cateOptions,
      subjectCate,
      isShowAns,
      showAns,
      loadMore,
      judgeQus,
      userChoice,
      solveLog,
      checkTagCount,
      goBack,
      onYearChange
    }
  }
})
</script>

<style scoped>
.wrapper {
  display: inline-block;
  margin-left: 10px;
  margin-top: 10px;
}

.wrapper span {
  letter-spacing: 4px;
}

.loading-more {
  text-align: center;
  margin-top: 12px;
  height: 32px;
  line-height:32px;
}

.problem {
  padding: 10px;
  box-shadow: rgba(0, 0, 0, 0.25) 0 3px 8px;
  margin-bottom: 40px;
}
.show-ans {
  cursor: pointer;
  padding: 2px;
  border-radius: 6px;
  background-color: #1abc9c;
  color: white;
}

.answer-pad {
  margin-top: 20px;
}

.answer-pad .provider {
  padding-top: 10px;
}

.answer-pad .provider span{
  font-size: 1.2em;
  padding: 2px;
  font-weight: bold;
  letter-spacing: 1px;
}

.answer-right {
  box-shadow: rgba(20, 220, 53, 0.8) 0 3px 8px;
}

.answer-wrong {
  box-shadow: rgba(215, 26, 26, 0.5) 0 3px 8px;
}

.go-back {
  z-index: 999;
  cursor: pointer;
  font-size: 20px;
  margin-bottom: 20px;
}
</style>
